<?php
declare (strict_types=1);

namespace addons\Gallery\driver;

use think\helper\Str;

class Compress
{
    private $src;

    private $image;

    private $imageinfo;

    protected $config;

    protected $path;

    protected $localPath;

    public function __construct($config)
    {
        $this->config = $config;
        $this->path = $this->config['url'] . date('Ymd') . '/';
        $this->localPath = $this->config['local'] . date('Ymd') . '/';
    }

    public function compressImg($file)
    {
        return $this->config['compress'] ? $this->localImageCompress($file) : $this->localImage($file);
    }

    public function localImage($file): array
    {
        $name = md5(time() . Str::random(32)) . '.' . $file['file']->getOriginalExtension();
        $result = $file['file']->move($this->localPath, $name);
        $url = $this->path . $result->getFileName();
        if (!$this->illegalCheck($url)) {
            return ['code' => -1, 'msg' => '请勿上传非法文件'];
        }
        return [
            'code' => 1,
            'msg' => 'success',
            'result' => [
                'name' => $name,
                'url' => $url
            ]
        ];
    }

    public function localImageCompress($file): array
    {
        try {
            $name = md5(time() . Str::random(32)) . '.' . $file['file']->getOriginalExtension();
            $this->src = $file['file']->getPathName();
            $this->_openImage();
            $url = $this->path . $name;
            if (!file_exists('.' . $this->path)) {
                mkdir('.' . $this->path, 0777, true);
            }
            if ($this->_saveImage('.' . $url)) {
                if (!$this->illegalCheck($url)) return ['code' => -1, 'msg' => '请勿上传非法文件'];
                return [
                    'code' => 1,
                    'msg' => 'success',
                    'result' => [
                        'name' => $name,
                        'url' => $url
                    ]];
            } else {
                return ['code' => -1, 'msg' => '上传失败'];
            }
        } catch (\Exception $e) {
            //压缩上传失败，使用不压缩重试
            return $this->localImage($file);
        }
    }

    public function compressVideo($file)
    {
        try {
            $name = md5(time() . Str::random(32)) . '.' . $file['file']->getOriginalExtension();
            $url = $this->config['url'] . date('Ymd') . '/';
            $local = $this->config['local'] . date('Ymd') . '/';
            $result = $file['file']->move($local, $name);

        } catch (\Exception $e) {

            return ['code' => -1, 'msg' => '上传失败：' . $e->getMessage()];
        }

        return ['code' => 1, 'msg' => 'success', 'result' => ['name' => $name, 'url' => $url . $result->getFileName()]];
    }

    /**
     * @param $disks -- 文件路径
     * @return bool
     * 验证图片是否含有非法文件
     */
    private function illegalCheck($disks): bool
    {
        $file = '.' . $disks;
        if (file_exists($file)) {
            $resource = fopen($file, 'rb');
            $fileSize = filesize($file);
            fseek($resource, 0);

            if ($fileSize > 512) {
                $hexCode = bin2hex(fread($resource, 512));
                fseek($resource, $fileSize - 512);
                $hexCode .= bin2hex(fread($resource, 512));
            } else { // 取全部
                $hexCode = bin2hex(fread($resource, $fileSize));
            }
            fclose($resource);

            $check = preg_match('#__HALT_COMPILER()#i', $hexCode)
                || preg_match('#/script>#i', $hexCode)
                || preg_match('#<([^?]*)\?php#i', $hexCode)
                || preg_match('#<\?\=#i', $hexCode);
            if ($check) {
                $this->error = '非法文件上传！';
                $this->remove($file);
                return false;
            }

            $result = preg_match($this->config['regular'], $hexCode);

            if ($result) {
                $this->error = '非法文件上传！';
                $this->remove($file);
                return false;
            } else {
                return true;
            }
        } else {
            return false;
        }
    }

    /**
     * @param $disks -- 文件路径
     * @return bool
     * 删除文件
     */
    private function remove($file): bool
    {
        if (file_exists($file)) {
            unlink($file);
            return true;
        } else {
            return false;
        }
    }

    /**
     * 内部：打开图片
     */
    private function _openImage()
    {
        [$width, $height, $type, $attr] = getimagesize($this->src);
        $this->imageinfo = [
            'width' => $width,
            'height' => $height,
            'type' => image_type_to_extension($type, false),
            'attr' => $attr
        ];
        $fun = "imagecreatefrom" . $this->imageinfo['type'];
        $this->image = $fun($this->src);
        $this->_thumpImage();
    }

    /**
     * 内部：操作图片
     */
    private function _thumpImage()
    {
        $new_width = intval($this->imageinfo['width'] * $this->config['percent']);
        $new_height = intval($this->imageinfo['height'] * $this->config['percent']);
        $image_thump = imagecreatetruecolor($new_width, $new_height);
        //将原图复制带图片载体上面，并且按照一定比例压缩,极大的保持了清晰度
        imagecopyresampled($image_thump, $this->image, 0, 0, 0, 0, $new_width, $new_height, $this->imageinfo['width'], $this->imageinfo['height']);
        imagedestroy($this->image);
        $this->image = $image_thump;
    }

    /**
     * 保存图片到硬盘：
     * @param string $dstImgName 1、可指定字符串不带后缀的名称，使用源图扩展名 。2、直接指定目标图片名带扩展名。
     */
    private function _saveImage($dstImgName)
    {
        if (empty($dstImgName)) return false;
        $allowImgs = ['.jpg', '.jpeg', '.png', '.bmp', '.wbmp', '.gif'];   //如果目标图片名有后缀就用目标图片扩展名 后缀，如果没有，则用源图的扩展名
        $dstExt = strrchr($dstImgName, ".");
        $sourseExt = strrchr($this->src, ".");
        if (!empty($dstExt)) $dstExt = strtolower($dstExt);
        if (!empty($sourseExt)) $sourseExt = strtolower($sourseExt);
        //有指定目标名扩展名
        if (!empty($dstExt) && in_array($dstExt, $allowImgs)) {
            $dstName = $dstImgName;
        } elseif (!empty($sourseExt) && in_array($sourseExt, $allowImgs)) {
            $dstName = $dstImgName . $sourseExt;
        } else {
            $dstName = $dstImgName . $this->imageinfo['type'];
        }
        $funcs = "image" . $this->imageinfo['type'];

        return $funcs($this->image, $dstName);

    }

    /**
     * 销毁图片
     */
    public function __destruct()
    {
        if (!empty($this->config['compress'])) imagedestroy($this->image);
    }
}